#include "clientupdate.h"
#include "ui_clientupdate.h"

ClientUpdate::ClientUpdate(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientUpdate),
    settingsVersions(nullptr),
    fileSum(0),
    value(0)
{
    ui->setupUi(this);
    this->setWindowFlags(Qt::WindowStaysOnTopHint);
    ui->label->setText("进度");
    ui->labelAutoUpdate->setText("自动更新中");
    ui->labelCurrentVersions->setText("当前版本1.0.0");
    ui->labelUpdateVersions->setText("");
    ui->pushButton->setText("检查更新");
    connect(ui->pushButton, SIGNAL(clicked(bool)), this, SLOT(onPushButton()));
    
    ui->progressBar->setValue(0);
}

ClientUpdate::~ClientUpdate()
{
    delete ui;
}

void ClientUpdate::onCuInit()
{
    Socket* socket = new Socket();
    QThread* thread = new QThread();
    socket->moveToThread(thread);
    connect(this, SIGNAL(signalSendToServer(QByteArray)),
            socket, SLOT(onSendToServer(QByteArray)));
    connect(socket, SIGNAL(signalSendToThread(QByteArray)),
            this, SLOT(onReadyRead(QByteArray)));
    connect(socket, SIGNAL(signalSendTcpState(QAbstractSocket::SocketState)),
            this, SLOT(onStateChanged(QAbstractSocket::SocketState)));
    connect(this, SIGNAL(signalSocketInit()), socket, SLOT(onSocketInit()));
    thread->start();
    emit signalSocketInit();

    currentVersions = "1.0.0";
}

void ClientUpdate::onPushButton()
{
    TcpData tData;
    tData.type = Type::VERSIONS_EXAMINE;
    FileData fData;
    QJsonObject jo;
    jo.insert("Versions", currentVersions);
    fData.fileContent = QJsonDocument(jo).toJson();
    tData.data = convert(fData);
    emit signalSendToServer(envelop(tData));
}

QByteArray ClientUpdate::convert(FileData fd)
{
    QByteArray data;
    data.append(convertToByte(fd.MD5.size()));
    data.append(fd.MD5);
    data.append((convertToByte(QByteArray().append(fd.fileName).size())));
    data.append(fd.fileName);
    data.append(fd.fileContent);
    return data;
}

FileData ClientUpdate::convert(QByteArray data)
{
    /*封包的时候获取前四个字节包含MD5大小所以获取后删掉在left读取以此类推*/
    FileData fData;
    if (data.size() < 8) {
        return fData;
    }
    qint32 ms = data.left(4).toHex().toInt(0, 16);
    data.remove(0, 4);
    fData.MD5 = data.left(ms);
    data.remove(0, ms);
    qint32 ns = data.left(4).toHex().toInt(0, 16);
    data.remove(0, 4);
    fData.fileName = QString::fromUtf8(data.left(ns));
    data.remove(0, ns);
    fData.fileContent = data;
    
    return fData;
}

QByteArray ClientUpdate::envelop(TcpData tData)
{
    QByteArray ret;
    QDataStream ds(&ret, QIODevice::WriteOnly);
    
    ds << tData.type << tData.data.size()
       << qChecksum(tData.data, tData.data.size());
    ret.prepend("$$$");
    ret.append(tData.data);
    return ret;
}

TcpData ClientUpdate::unpack(QByteArray &data)
{
    TcpData tDAta;
    tDAta.type = Type::INVALID;
    while ((data.size() > 3) && !data.startsWith("$$$")) {
        data.remove(0, 1);
    }
    
    if (data.size() < 13) {
        return tDAta;
    }
    /*cs获取封包的tData.data.size*/
    qint32 cs = data.mid(7, 4).toHex().toInt(0, 16);
    if (cs < 1) {
        data.remove(0, 3);
        return tDAta;
    }
    /*如果总包大小小于 13+cs就是错误数据   13*/
    if (data.size() < (13 + cs)) {
        return tDAta;
    }
    
    data.remove(0, 3);
    QDataStream ds(&data, QIODevice::ReadOnly);
    quint16 checkNum = 0;
    
    int type;
    ds >> type >> cs >> checkNum;
    tDAta.type = Type(type);
    
    //4+4+2
    data.remove(0, 10); //删除type和type大小和校验
    /*数据赋值，删除*/
    tDAta.data = data.left(cs);
    data.remove(0, cs);
    
    //数据校验判断
    if (checkNum != qChecksum(tDAta.data, tDAta.data.size())) {
        tDAta.type = Type::INVALID;
        tDAta.data.clear();
        return tDAta;
    }
    
    return tDAta;
}

const QString ClientUpdate::countMd5(const QString filePath)
{
    if (QFileInfo(filePath).exists()) {
        QFile f(filePath);
        QByteArray fileData;
        if (f.open(QIODevice::ReadOnly)) {
            fileData = f.readAll();
            f.close();
        }
        QCryptographicHash cgh(QCryptographicHash::Md5);
        cgh.addData(fileData);
        return cgh.result().toHex();
    }
    return "";
}

void ClientUpdate::typeUpdate(FileData fData)
{
    QJsonObject joData = QJsonDocument::fromJson(fData.fileContent).object();
    //qDebug() << joData;
    if (joData.contains("versions")) {
        //版本对比  此处if为了从注册表获取版本
        //if (settingsVersions->contains("Versions")) {
        if (1) {
            //settingsVersions->value("Versions").toString();
            QString temp = currentVersions;
            temp.remove('.'); //剔除版本号的特殊字符
            updateVersions = joData["versions"].toString();
            updateVersions.remove('.');
            qDebug() << updateVersions << "---" << temp;
            if (updateVersions.toInt() > temp.toInt()) {
                ui->labelUpdateVersions->setText(QString("更新版本%1").arg(joData["versions"].toString()));
                this->show();
                
                /*数据发送*/
                TcpData tempTData;
                tempTData.type = Type::UPDATE;
                tempTData.data = convert(fData);
                emit signalSendToServer(envelop(tempTData));
                
                fileSum = fData.fileName.toUtf8().toHex().toInt();
                value = 100.0 / fileSum;
            }
            if (updateVersions.toInt() == temp.toInt()) {
                QMessageBox::about(this, "检测结果", "已是最新版本");
            }
        }
    }
}

void ClientUpdate::typeData(FileData fData)
{
    qDebug() << "typeData";
    QString localPath = (QString("./%1").arg(fData.fileName));
    QFile f(localPath);
    if (QFileInfo(localPath).exists()) {
        if (f.open(QIODevice::WriteOnly | QIODevice::Append)) {
            f.write(fData.fileContent);
            f.close();
        }
        else {
            //文件打开失败
        }
    }
    else {
        //传输中文件丢失
    }
}

void ClientUpdate::typeStart(FileData fData)
{
    QString localPath = (QString("./%1").arg(fData.fileName));
    QFile f(localPath);
    
    TcpData tData;
    tData.type = Type::START;
    
    if (QFileInfo(localPath).exists()) {
        if (fData.MD5 == countMd5(localPath)) {
            //如果相等发送结束
            fileSum--;
            tData.type = Type::FINISH;
            tData.data = convert(fData);
            emit signalSendToServer(envelop(tData));
            
            ui->progressBar->setValue(value + ui->progressBar->value());
            /*判断文件数量*/
            if (fileSum <= 0) {
                qMessageBox();
                /*写入注册表*/
                currentVersions = updateVersions;
                qDebug() << currentVersions << "123123123123";
            }
            return;
        }
        else {
            f.open(QIODevice::WriteOnly | QIODevice::Truncate);
            f.close();
            tData.data = convert(fData);
            emit signalSendToServer(envelop(tData));
            return;
        }
    }
    else {
        QString path = QFileInfo(localPath).path();
        if (QDir().mkpath(path)) {
            f.open(QIODevice::WriteOnly);
            f.close();
            tData.data = convert(fData);
            emit signalSendToServer(envelop(tData));
            return;
        }
        else {
            qDebug() << "文件夹创建失败";
            //error
        }

    }
}

void ClientUpdate::typeFinish(FileData fData)
{
    qDebug() << "typeFinish";
    QString localPath = (QString("./%1").arg(fData.fileName));
    
    TcpData tData;
    tData.type = Type::FINISH;
    tData.data = convert(fData);
    
    if (QFileInfo(localPath).exists()) {
        if (fData.MD5 == countMd5(localPath)) {
            //如果相等发送结束
            fileSum--;
            
            emit signalSendToServer(envelop(tData));
            
            ui->progressBar->setValue(value + ui->progressBar->value());
            if (fileSum <= 0) {
                qMessageBox();
                /*写入注册表*/
                currentVersions = updateVersions;
                qDebug() << currentVersions << "123123123123";
            }
            return;
        }
        else {
            //发送结束后md5不匹配，那么直接删掉重传
            QDir().remove(localPath);
            tData.type = Type::START;
            emit signalSendToServer(envelop(tData));
        }
    }
    else {
        //文件不存在
        
    }
}

void ClientUpdate::qMessageBox()
{
    ui->progressBar->setValue(100);
    QMessageBox* box = new QMessageBox(QMessageBox::Information, "提示", "客户端更新完成",QMessageBox::Yes, this);

    box->button(QMessageBox::Yes)->setText("启动客户端");
    box->button(QMessageBox::No)->setText("不想启动");
    box->button(QMessageBox::Yes)->setFocus(); //设置焦点
    //此move会有变动
    int ret = box->exec();
    if (ret == QMessageBox::Yes) {
        /*启动程序*/

        this->close();
    }
}

void ClientUpdate::datProcessing(TcpData tData)
{
    qDebug() << tData.type << "------";
    FileData fData = convert(tData.data);
    switch (tData.type) {
    case Type::UPDATE: {
        typeUpdate(fData);
    }
        break;
    case Type::DATA: {
        typeData(fData);
    }
        break;
    case Type::START: {
        typeStart(fData);
    }
        break;
    case Type::FINISH: {
        typeFinish(fData);
    }
        break;
    default:
        break;
    }
}

void ClientUpdate::onReadyRead(QByteArray data)
{
    sourceData.append(data);
    TcpData tData = unpack(sourceData);
    /*拆包后如果正常拆出进入循环发送数据后继续拆包，为了防止粘包处理*/
    while (Type::INVALID != tData.type) {
        datProcessing(tData);
        tData = unpack(sourceData);
    }
}

void ClientUpdate::onStateChanged(QAbstractSocket::SocketState socketState)
{
    qDebug() << socketState;
    switch (socketState) {
    case QAbstractSocket::ConnectedState:
        qDebug() << "服务端连接成功";
        break;
    case QAbstractSocket::ClosingState:
    case QAbstractSocket::UnconnectedState:
        qDebug() << "服务端断开";
        break;
    default:
        break;
    }
}

template<typename T>
QByteArray ClientUpdate::convertToByte(T v)
{
    QByteArray ret;
    for (size_t i = 0; i < sizeof(T); i++) {
        ret.prepend(char(v >> (i * 8)));
    }
    
    return ret;
}
